package cc.eguid.tools;

import cc.eguid.tools.msg.*;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 文件传输服务端 - 独立端口处理文件传输
 */
public class FileTransferServer implements Runnable {
    private int filePort;
    private ServerSocket fileServerSocket;
    private Socket fileClientSocket;
    private ObjectOutputStream out;
    private ObjectInputStream in;
    private boolean running;
    private String validToken;

    public FileTransferServer(int filePort, String token) {
        this.filePort = filePort;
        this.validToken = token;
    }

    @Override
    public void run() {
        try {
            fileServerSocket = new ServerSocket(filePort);
            running = true;
            System.out.println("文件传输服务启动，端口: " + filePort);

            while (running) {
                try {
                    fileClientSocket = fileServerSocket.accept();
                    System.out.println("文件传输客户端已连接");
                    
                    out = new ObjectOutputStream(fileClientSocket.getOutputStream());
                    in = new ObjectInputStream(fileClientSocket.getInputStream());

                    // 处理文件传输请求
                    handleFileTransfer();
                    
                } catch (IOException e) {
                    if (running) {
                        e.printStackTrace();
                    }
                } finally {
                    closeFileConnection();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            stop();
        }
    }

    private void handleFileTransfer() {
        try {
            // 等待客户端发送令牌
            Object obj = in.readObject();
            if (obj instanceof TokenMessage) {
                TokenMessage tokenMsg = (TokenMessage) obj;
                if (!validToken.equals(tokenMsg.getToken())) {
                    // 令牌验证失败
                    out.writeObject(new AuthMessage(false, "令牌错误"));
                    out.flush();
                    System.out.println("文件传输客户端令牌验证失败");
                    return;
                }
            } else {
                // 协议错误
                out.writeObject(new AuthMessage(false, "协议错误"));
                out.flush();
                return;
            }

            // 发送令牌验证成功响应
            out.writeObject(new AuthMessage(true, "文件传输验证成功"));
            out.flush();
            System.out.println("文件传输客户端令牌验证通过");

            // 处理文件上传请求
            Object uploadObj = in.readObject();
            if (uploadObj instanceof FileUploadMessage) {
                handleFileUpload((FileUploadMessage) uploadObj);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void handleFileUpload(FileUploadMessage uploadMessage) {
        try {
            // 验证上传路径安全性
            String safePath = validateUploadPath(uploadMessage.getTargetPath());
            File uploadDir = new File(safePath);

            // 确保上传目录存在
            if (!uploadDir.exists()) {
                if (!uploadDir.mkdirs()) {
                    sendUploadResponse(false, "无法创建上传目录: " + safePath);
                    return;
                }
            }

            // 检查目录是否可写
            if (!uploadDir.canWrite()) {
                sendUploadResponse(false, "上传目录不可写: " + safePath);
                return;
            }

            // 检查磁盘空间
            long freeSpace = uploadDir.getFreeSpace();
            if (freeSpace < uploadMessage.getFileSize()) {
                sendUploadResponse(false, "磁盘空间不足。需要: " +
                        uploadMessage.getFileSize() + " 字节, 可用: " + freeSpace + " 字节");
                return;
            }

            // 接受上传请求
            sendUploadResponse(true, "准备接收文件");

            // 接收文件数据
            File outputFile = new File(uploadDir, uploadMessage.getFileName());
            try (FileOutputStream fileOutputStream = new FileOutputStream(outputFile)) {
                byte[] buffer = new byte[8192];
                long remaining = uploadMessage.getFileSize();

                while (remaining > 0) {
                    int bytesToRead = (int) Math.min(buffer.length, remaining);
                    int bytesRead = in.read(buffer, 0, bytesToRead);
                    if (bytesRead == -1) {
                        throw new IOException("文件传输意外中断");
                    }
                    fileOutputStream.write(buffer, 0, bytesRead);
                    remaining -= bytesRead;
                }
            }

            // 发送上传成功确认
            sendUploadResult(true, "文件上传成功: " + outputFile.getAbsolutePath());
            System.out.println("文件上传完成: " + outputFile.getAbsolutePath());

        } catch (Exception e) {
            try {
                sendUploadResult(false, "文件上传失败: " + e.getMessage());
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
            e.printStackTrace();
        }
    }

    // 添加上传路径验证方法（安全考虑）
    private String validateUploadPath(String requestedPath) {
        if (requestedPath == null || requestedPath.trim().isEmpty()) {
            // 使用当前用户目录作为默认路径
            return System.getProperty("user.home") + File.separator + "uploads";
        }

        // 防止路径遍历攻击
        String normalizedPath = new File(requestedPath).getAbsolutePath();
        if (normalizedPath.contains("..")) {
            System.out.println("检测到可疑路径，使用默认路径: " + requestedPath);
            return System.getProperty("user.home") + File.separator + "uploads";
        }

        return normalizedPath;
    }

    // 添加上传响应发送方法
    private void sendUploadResponse(boolean accepted, String message) throws IOException {
        out.writeObject(new FileUploadResponse(accepted, message));
        out.flush();
    }

    // 添加上传结果发送方法
    private void sendUploadResult(boolean success, String message) throws IOException {
        out.writeObject(new FileUploadResult(success, message));
        out.flush();
    }

    private void closeFileConnection() {
        try {
            if (in != null) in.close();
            if (out != null) out.close();
            if (fileClientSocket != null) fileClientSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void stop() {
        running = false;
        try {
            closeFileConnection();
            if (fileServerSocket != null) fileServerSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

